package org.jivesoftware.spark.crowd.ui;

import java.awt.Color;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;

import javax.swing.Icon;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.jivesoftware.resource.SparkRes;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.spark.ChatManager;
import org.jivesoftware.spark.PresenceManager;
import org.jivesoftware.spark.SparkManager;
import org.jivesoftware.spark.component.Table;
import org.jivesoftware.spark.crowd.CrowdRecorder;
import org.jivesoftware.spark.crowd.CrowdsManager;
import org.jivesoftware.spark.crowd.Param;
import org.jivesoftware.spark.crowd.impl.Crowd;
import org.jivesoftware.spark.crowd.impl.Member;
import org.jivesoftware.spark.crowd.packet.CrowdsIQManager;
import org.jivesoftware.spark.crowd.packet.CrowdsPresenceManager;
import org.jivesoftware.spark.crowd.packet.DiscoverCrowds;
import org.jivesoftware.spark.crowd.packet.MParser;
import org.jivesoftware.spark.crowd.packet.PacketAssembler;
import org.jivesoftware.spark.crowd.packet.ReceivedPacketProcesser;
import org.jivesoftware.spark.crowd.packet.DiscoverCrowds.CrowdItem;
import org.jivesoftware.spark.ui.ChatContainer;
import org.jivesoftware.spark.ui.ChatRoom;
import org.jivesoftware.spark.ui.ChatRoomListener;
import org.jivesoftware.spark.ui.ChatRoomNotFoundException;

public class UIEventManager {

	private CrowdsManager crowdsManager;
	private static UIEventManager instance;
	private CrowdsIQManager iqManager;

	private PacketAssembler pa;

	public static UIEventManager getInstance() {
		if (instance == null)
			instance = new UIEventManager();
		return instance;
	}

	private UIEventManager() {

	}

	public void crowdLabelDoubleClick(String labelName) {

		try {
			openRoom(labelName);
		} catch (Exception e) {
			e.printStackTrace();
			CrowdRecorder
					.record("UIEventManager crowdLabelDoubleClick error : "
							+ e.getMessage());
		}
	}

	/**
	 * 
	 */
	public void creatNewCrowd(final String crowdName, final String title) {
		// TODO
		try {
			checkNull(crowdName);
			checkExists(crowdName);

			IQ createNewCrowd = new IQ() {
				@Override
				public String getChildElementXML() {
					return pa.getCreatePacket(crowdName, title);
				}
			};
//			createNewCrowd.setFrom(Param.getBareAddress());
//			createNewCrowd.setTo(Param.getFullCrowdDomain());
			createNewCrowd.setTo("127.0.0.1");
			createNewCrowd.setType(IQ.Type.SET);

			IQ result = CrowdsIQManager.getInstance().sendAndWait(
					createNewCrowd);

			Member creator = new Member(Param.getBareAddress(), Param
					.getNickName());
			Crowd newCrowd = new Crowd(crowdName, creator, title);
			List<Crowd> list = new ArrayList<Crowd>();
			list.add(newCrowd);
			crowdsManager.receiveCrowdsLocal(list);

			JOptionPane.showMessageDialog(null, "群已创建");
		} catch (XMPPException e) {
			if (e.getXMPPError() != null) {
				CrowdRecorder.record(" UIEventManager creatNewCrowd "+e.getXMPPError().getCode());
				CrowdRecorder.record(" UIEventManager creatNewCrowd "+e.getXMPPError().getMessage());
				if(e.getXMPPError().getCode() == 405)
					JOptionPane.showMessageDialog(null, "群名称重复");
				else	
					JOptionPane.showMessageDialog(null, "发生一个错误，群无法创建，请查看错误日志了解更多信息。");
			} else {
				JOptionPane.showMessageDialog(null, "发生一个错误，群无法创建，请查看错误日志了解更多信息。");
			}
			e.printStackTrace();
		} catch (Exception e) {
			CrowdRecorder.record("UIEventManager creatNewCrowd : "
					+ e.getMessage());
			JOptionPane.showMessageDialog(null, "群名称重复");
			e.printStackTrace();
		}

	}

	/**
	 * the server will just send a {@link Message}(have a
	 * {@link PacketExtension}) to the crowd's creator after receiving this
	 * {@link IQ}
	 */
	
	public void IandA(final String crowdName,final String toJID,final String xmlns){
		try {
			checkNull(crowdName);
			checkExists(crowdName);
		} catch (IllegalStateException e) {
			e.printStackTrace();
			JOptionPane.showMessageDialog(null, "已经加入这个群");
			return;
		}catch (IllegalArgumentException ee){
			ee.printStackTrace();
			return;
		}

		Message ianda = new Message() {
			@Override
			protected synchronized String getExtensionsXML() {
				
				return pa.getIAndAPacket(crowdName,xmlns);
			}			
		};
		ianda.setBody(" ");
		ianda.setFrom(Param.getBareAddress());
		ianda.setTo(toJID);
		ianda.setType(Message.Type.chat);

		Param.getConnection().sendPacket(ianda);
	}

	/**
	 * return the invite or apply result to server
	 * 
	 * @param crowdName
	 * @param creatorJID
	 * @param newMemberJID
	 * @param result
	 * @param ns
	 */
	public void sendResult(final String crowdName, final String toJID,
			final String result, final String ns) {

		IQ reply = new IQ() {
			@Override
			public String getChildElementXML() {
				return pa.getSendResult(crowdName, toJID,
						result, ns);
			}
		};

		reply.setFrom(Param.getBareAddress());
		reply.setTo(Param.getConnection().getServiceName());
		reply.setType(IQ.Type.SET);

		iqManager.send(reply);

	}

	

	/**
	 * 
	 */
	public void giveUPMembership(final String crowdName) {
		try {
			checkNull(crowdName);
		} catch (Exception e) {
			e.printStackTrace();
			return;
		}

		IQ quit = new IQ() {
			@Override
			public String getChildElementXML() {
				return pa.getQuitPacket(crowdName);
			}
		};
		quit.setFrom(Param.getBareAddress());
		quit.setTo(Param.getConnection().getServiceName());
		quit.setType(IQ.Type.SET);

		try {
			iqManager.sendAndWait(quit);

			crowdsManager.removeCrowd(crowdName);

		} catch (XMPPException e) {
			if (e.getXMPPError() != null)
				CrowdRecorder.record("UIEventManager giveUPMembership error : "
						+ e.getXMPPError().getMessage());
			else
				CrowdRecorder.record("UIEventManager giveUPMembership error : "
						+ e.getMessage());
			e.printStackTrace();
		}

	}

	/**
	 * only creator can dismiss a crowd
	 */
	public void dismissCrowd(final String crowdName) {
		try {
			checkNull(crowdName);
			boolean exists = false;
			Crowd crowd = null;
			for (Crowd c : crowdsManager.getLocalCrowds())
				if (c.getName().equalsIgnoreCase(crowdName)) {
					exists = true;
					crowd = c;
					break;
				}
			if (!exists)
				throw new IllegalStateException("本地没有这个群");
			if (!crowd.getCreator().getJID().equals(Param.getBareAddress()))
				throw new IllegalStateException("没有权限");

			IQ destroy = new IQ() {
				@Override
				public String getChildElementXML() {
					return pa.getDismissPacket(crowdName);
				}
			};
			destroy.setTo(Param.getConnection().getServiceName());
			destroy.setFrom(Param.getBareAddress());
			destroy.setType(IQ.Type.SET);

//			IQ result = iqManager.sendAndWait(destroy);
			iqManager.send(destroy);
//			crowdsManager.removeCrowd(crowdName);
		/*} catch (XMPPException e) {
			if (e.getXMPPError() != null) {
				JOptionPane.showMessageDialog(null, e.getXMPPError()
						.getMessage());
			} else {
				JOptionPane.showMessageDialog(null, e.getMessage());
			}
			e.printStackTrace();
			e.printStackTrace();*/
		} catch (Exception e) {
			CrowdRecorder.record("UIEventManager dismissCrowd error : "
					+ e.getMessage());
			e.printStackTrace();
		}
	}

	public void showCrowdInfo(String crowdName) {
		// TODO
	}

	/**
	 * 
	 */
	public void openRoom(final String roomName) {
		try {
			showCrowdChatRoom(roomName);
		} catch (ChatRoomNotFoundException e1) {
			// TODO check if the window is already exist , or is registered to
			// ChatManager but just not visible
			final CrowdChatRoom crowdChatRoom = new CrowdChatRoom(roomName);
			crowdChatRoom.decorate();

			final ChatContainer chatContainer = SparkManager.getChatManager()
					.getChatContainer();
			chatContainer.addChatRoomListener(new ChatRoomListener() {
				@Override
				public void chatRoomActivated(ChatRoom room) {
					// TODO Auto-generated method stub

				}

				@Override
				public void chatRoomClosed(ChatRoom room) {
					if (room instanceof CrowdChatRoom) {
						CrowdChatRoom ccr = (CrowdChatRoom) room;
						ccr.getMemberListPane().getListModel()
								.removeAllElements();

						CrowdsPresenceManager.PresenceUpdater pu = ReceivedPacketProcesser
								.getInstance().removeCrowdPresenceListener(
										room.getRoomname());
						CrowdsPresenceManager.getInstance()
								.removePresenceUpdater(pu);
						crowdsManager.findCrowdByName(ccr.getRoomname()).removeAllMembers();
					}
					chatContainer.removeChatRoomListener(this);

				}

				@Override
				public void chatRoomLeft(ChatRoom room) {
					// TODO Auto-generated method stub
				}

				@Override
				public void chatRoomOpened(ChatRoom room) {
					// TODO Auto-generated method stub
				}

				@Override
				public void userHasJoined(ChatRoom room, String userid) {
					// TODO Auto-generated method stub

				}

				@Override
				public void userHasLeft(ChatRoom room, String userid) {
					// TODO Auto-generated method stub

				}

			});

			chatContainer.addChatRoom(crowdChatRoom);
			chatContainer.activateChatRoom(crowdChatRoom);

			/*
			 * at last ,send packet to load members. DO NOT let this thread
			 * start before the crowdChatRoom is shown on the ChatContainer.
			 */
			new Thread(new Runnable() {

				@Override
				public void run() {
					try {
						/*
						 * add presence listener to this crowd chat room
						 */
						CrowdsManager.getInstance().addCrowdPresenceListener(
								roomName);
						/*
						 * load members
						 */
						CrowdsManager.getInstance().loadMembers(roomName);
						synchronized(instance){
							instance.wait();
						}
						Crowd crowd = crowdsManager.findCrowdByName(roomName);
						if(crowd != null){
							if(crowd.isMembersLoaded()){
								List<Message> removeMessages = new ArrayList<Message>();
								List<Message> delayedMessages = ReceivedPacketProcesser.getInstance().getLocalDelayMessages();
								for(Message dm : delayedMessages){
									
									String delayCrowdName = "";
									String delayMemberJID = "";
									PacketExtension delayExt = dm.getExtension(Param.CROWD_SERVICE_NAME, Param.message_NS);
									if (delayExt instanceof MParser) {
										MParser delayMp = (MParser) delayExt;
										delayCrowdName = delayMp.getCrowdName();
										delayMemberJID = delayMp.getMemberJID();
									}
									if(delayCrowdName.equals(roomName) == false){
										continue;
									}
									Crowd currCrowd = CrowdsManager.getInstance().findCrowdByName(delayCrowdName);
									
									Member currMember = currCrowd.findMemberByJID(delayMemberJID);
									String nickname = "";
									if(currMember != null)								
										nickname = currMember.getNickName();
									else nickname = SparkManager.getUserManager().getNickname(delayMemberJID);
									if (nickname == null)
										nickname = delayMemberJID;
									// TODO have not considered super link like URL ,http://
									// and image icons
									
									
									crowdChatRoom.getTranscriptWindow().insertMessage(nickname, dm,ChatManager.FROM_COLOR, new Color(0, 0, 0, 0));
									removeMessages.add(dm);																	
								}
								delayedMessages.removeAll(removeMessages);	
								synchronized(delayedMessages){
								delayedMessages.notifyAll();
								}
							}
						}
					} catch (Exception e) {
						CrowdRecorder
								.record("can't load members. crowd name : "
										+ roomName);
						e.printStackTrace();
					}
				}

			}).start();

		}

	}

	private void showCrowdChatRoom(String roomName)
			throws ChatRoomNotFoundException {
		
		ChatRoom crowdChatRoom = Param.getChatContainer().getChatRoom(roomName);
		Param.getChatContainer().activateChatRoom(crowdChatRoom);
	}

	/**
	 * 
	 * @param presence
	 * @param roomName
	 * @param nickName
	 */
	public void handleSelfPresenceChanged(Presence presence) {
		Icon icon = PresenceManager.getIconFromPresence(presence);
		String nickName = Param.getNickName();
		Collection<ChatRoom> rooms = Param.getChatContainer().getChatRooms();
		for (ChatRoom room : rooms) {
			if ((room instanceof CrowdChatRoom)) {
				CrowdChatRoom crowdChatRoom = (CrowdChatRoom) room;
				MemberListPane listPane = crowdChatRoom.getMemberListPane();
				int index = listPane.getIndex(nickName);
				if (index != -1) {
					final JLabel userLabel = new JLabel(nickName, icon,
							JLabel.HORIZONTAL);
					listPane.getListModel().setElementAt(userLabel, index);
				}
			}
		}
	}

	public void search(final String contained, final SearchDialog dlg) {
		
		IQ search = new IQ() {
			@Override
			public String getChildElementXML() {
				// TODO Auto-generated method stub
				return pa.getSearchPacket(contained);
			}
		};
//		search.setTo(Param.getFullCrowdDomain());
		search.setTo(Param.getConnection().getServiceName());
//		search.setFrom(Param.getBareAddress());
		search.setType(IQ.Type.GET);

		try {
			IQ searchResult = CrowdsIQManager.getInstance().sendAndWait(search);
			if (searchResult instanceof DiscoverCrowds) {
				DiscoverCrowds dcs = (DiscoverCrowds) searchResult;
				if (!dcs.getXmlns().equals(Param.search_NS)) {
					CrowdRecorder
							.record("UIEventManager search xml error : search result's xmlns is not http://www.cecise.com/ceciseim/search");
					CrowdRecorder
							.recordOnConsol("UIEventManager search xml error : search result's xmlns is not http://www.cecise.com/ceciseim/search");
				}
				ArrayList<Object> crowds = new ArrayList<Object>();
				// "名称","创建人","主题","创建时间"
				Iterator<CrowdItem> items = dcs.getItems();
				while (items.hasNext()) {
					CrowdItem item = (CrowdItem) items.next();
					String[] crowd = new String[4];

					crowd[0] = item.getName();
					crowd[1] = item.getCreatorNickName();
					crowd[2] = item.getTitle();
					
					String time_millis = item.getCreateTime();
					Calendar cld = new GregorianCalendar();
					cld.setTimeInMillis(Long.valueOf(time_millis));
					String createTime = cld.get(Calendar.YEAR) + "/" + (cld.get(Calendar.MONTH) + 1) + "/" + cld.get(Calendar.DAY_OF_MONTH);
					crowd[3] = createTime;
					
					
					crowds.add(crowd);
					dlg.saveCreatorJID(crowd[0], item.getCreatorJID());
					
				}
				dlg.getResultTable().clearTable();
				dlg.getResultTable().add(crowds);

			} else {
				CrowdRecorder
						.record("UIEventManager search ClassCast error : search result not instanceof DiscoverCrowds.");
				CrowdRecorder
						.recordOnConsol("UIEventManager search ClassCast error : search result not instanceof DiscoverCrowds.");
			}

		} catch (XMPPException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	/**
	 * 
	 * @param crowdName
	 * @throws IllegalArgumentException
	 */
	private void checkNull(String crowdName) throws IllegalArgumentException {
		if (crowdName == null || crowdName.equals(""))
			throw new IllegalArgumentException("群名称不能为空");

	}

	/**
	 * check if the crowdName already exists in crowdsLocal
	 * 
	 * @param crowdName
	 *            the crowd name will be checked
	 * @throws IllegalStateException
	 *             if the name already exists
	 */
	private void checkExists(String crowdName) throws IllegalStateException {
		for (Crowd c : crowdsManager.getLocalCrowds())
			if (c.getName().equalsIgnoreCase(crowdName))
				throw new IllegalStateException("群名称重复");
	}

	public void init() {
		crowdsManager = CrowdsManager.getInstance();
		this.iqManager = CrowdsIQManager.getInstance();
		this.pa = PacketAssembler.getinstance();
		CrowdList.getInstance();
	}

	private class FileSharer {
		private ServerSocket serverSocket;
		private Socket socket;
		
		
		public void updateFile(final File updateFile) {
			if (!updateFile.exists() || !updateFile.isFile())
				return;
			long count = updateFile.length();
			System.out.println("file size(byte) : " + count);

			if (count >= Integer.MAX_VALUE) {
				JOptionPane.showMessageDialog(null, "文件太大了");
				return;
			}
			Thread updateThread = new Thread(new Runnable() {
				@Override
				public void run() {
					try {
						
							OutputStream out = negotiateStream(updateFile);
							if(out == null)
								return;
							InputStream in = new FileInputStream(updateFile);
							System.out.println("out in ok");
							try {
								writeToStream(in, out);
							} catch (XMPPException e) {
								e.printStackTrace();
							} finally {
								try {
									if (in != null) {
										in.close();
									}

									out.flush();
									out.close();
									
									if(socket != null)
										socket.close();
									System.out.println("over!");
								} catch (IOException e) {
									/* Do Nothing */
								}
							}
						

						
					} catch (IOException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
				}

			});

			updateThread.start();

		}

		private OutputStream negotiateStream(File f) {
			
			OutputStream out = null;
			try {				
				final PostMethod post = new PostMethod("http://127.0.0.1:9090/servlet/crowd");
				String fileName = f.getName();
				Charset charSet = Charset.forName("utf-8");
				ByteBuffer bb = charSet.encode(fileName);
				byte[] byteName = bb.array();
				System.out.println(Arrays.toString(byteName));
				
				
				
				final HttpClient httpclient = new HttpClient();
				int status = httpclient.executeMethod(post);
				if(status != 200)					
					return null;				
				System.out.println(status);
				String setName = post.getResponseBodyAsString();
				System.out.println("set name = " + setName);
				socket = new Socket("127.0.0.1",9999);
				
				System.out.println("connected ...");
				out = socket.getOutputStream();
			} catch (Exception e) {
				e.printStackTrace();
			}
			return out;
		}

		protected void writeToStream(final InputStream in,
				final OutputStream out) throws XMPPException {
			System.out.println("write to stream");
			int BUFFER_SIZE = 4096;
			final byte[] b = new byte[BUFFER_SIZE];
			int count = 0;
			int amountWritten = 0;

			do {
				System.out.println("running...");
				// write to the output stream
				try {
					out.write(b, 0, count);
				} catch (IOException e) {
					throw new XMPPException("error writing to output stream", e);
				}

				amountWritten += count;

				// read more bytes from the input stream
				try {
					count = in.read(b);
				} catch (IOException e) {
					throw new XMPPException("error reading from input stream",
							e);
				}
			} while (count != -1);

			// the connection was likely terminated abrubtly if these are not
			// equal

		}
	}

	public static void main(String[] args) {
		UIEventManager uiem = UIEventManager.getInstance();
		FileSharer sharer = uiem.new FileSharer();
		JFileChooser chooser = new JFileChooser();

		int returnVal = chooser.showOpenDialog(null);
		if (returnVal == JFileChooser.APPROVE_OPTION) {

			File file = chooser.getSelectedFile();
			sharer.updateFile(file);
		}
	}

}
